iT邦幫忙

0

Day 20~22|Discord Bot 與 Google Calendar:模組化、日期驗證與錯誤處理

  • 分享至 

  • xImage
  •  

前一階段已經完成 Google OAuth 2.0 授權,也成功透過 Calendar API 建立行程。接下來,我想把這條流程整理成可以持續擴充的 Discord Bot。

這次將 Day 20~22 合併成一篇紀錄,實作仍分成三個階段:API 模組化、日期時間驗證,以及新增結果與錯誤處理。

我的完成標準是:程式寫好之後,還要測試成功。因此,除了自動測試,我也實際在 Discord 輸入 /add,再到 Google 日曆確認結果。

三天的實作目標

天數 主題 完成內容
Day 20 Google Calendar API 模組化 將 API 操作獨立成 Service,讓 Discord 指令呼叫
Day 21 日期、時間與時區驗證 檢查格式、真實日期、過去時間、台灣時區與跨日
Day 22 回覆與錯誤處理 顯示結果與連結,處理 API 失敗、授權失效和重複提交

Day 20|將 Google Calendar API 獨立成模組

1. 分開指令處理與 API 操作

原本 /add 同時負責讀取 Discord 選項、驗證資料,以及呼叫 Google API。功能增加後,這個檔案會越來越難維護。

這次把程式分成幾個部分:

src/
├── commands/
│   ├── add.js                  # 讀取指令、驗證與回覆
│   └── help.js
├── services/
│   └── calendarService.js      # 新增、查詢確認與錯誤分類
├── eventTime.js                # 日期、時間及時區驗證
├── googleCalendar.js           # OAuth 與 Calendar client
└── index.js                    # Bot 啟動與指令分派

calendarService.js 封裝 createEvent(),接收已驗證的行程資料,再透過 events.insert() 建立事件。Discord 指令只需要取得結果,不必知道 Google API 的細節。

以下是 add.js 呼叫 Service 的核心片段:

result = await service.createEvent(
  input,
  `${interaction.guildId || 'dm'}:${interaction.user.id}`
);

第二個參數用來區分伺服器與使用者,後續也會用在重複提交判斷。

Day 20:VS Code 模組化結構與 createEvent

圖 1:VS Code 中的模組化結構,以及 Calendar Service 的 createEvent()。

2. 沿用既有 OAuth 授權

Calendar client 會讀取既有的 token.json,並讓 Google 授權套件處理 Token 更新,不需要每次新增行程都重新開啟授權頁面。若授權遭撤銷或失效,才提示管理員重新授權。

這次先確認既有 Token 可用,再直接從 Discord 測試新增:

/add title:Day20-22_Taipei_CrossDay date:20261020 time:2330

Discord 回覆新增成功,也提供了 Google Calendar 的事件連結。接著使用 API 讀回事件,並開啟日曆網頁確認,確保行程真的寫進去。

Day 20:Discord add 實際新增成功

圖 2:Discord 實際執行 /add 成功,顯示日期、開始與結束時間、時區及事件連結。

Day 21|日期、時間與台灣時區驗證

1. 格式正確,不代表日期真的存在

20260230 雖然是八位數,卻不是有效日期。因此,我把驗證分成幾個層次:

驗證項目 規則或範例
日期格式 必須是八位數 YYYYMMDD
真實日期 拒絕 20260230、20261301,並檢查閏年
時間格式 必須是四位數 HHmm,採用 24 小時制
時間範圍 拒絕 2460、2560 等不存在的時間
過去時間 比較完整時間點,拒絕小於或等於現在的時間
行程名稱 去除首尾空白,長度須為 1~100 個字元

驗證失敗就直接回覆錯誤,不會繼續呼叫 Calendar Service。

我在 Discord 實際測試了不存在的日期、過去時間和錯誤時間,三種情況都被正確擋下。

Day 21:拒絕錯誤日期、過去時間與錯誤時間

圖 3:Discord 拒絕錯誤日期、過去時間及 2460,沒有將錯誤輸入送去新增行程。

2. 明確使用 Asia/Taipei

日期時間不能依賴執行電腦的預設時區,否則換到其他伺服器後,行程可能會偏移。

這次使用 Luxon,明確以 Asia/Taipei 解讀輸入,再產生包含 UTC offset 的時間字串。API 回傳的時間也會轉回台灣時間,統一顯示在 Discord。

const start = DateTime.fromObject(parts, {
  zone: 'Asia/Taipei'
});

const end = start.plus({ hours: 1 });

目前每筆行程固定持續一小時,所以跨日也必須正確處理。例如這次的實測:

開始:2026-10-20 23:30
結束:2026-10-21 00:30
時區:Asia/Taipei(UTC+08:00)

Day 21:Google 日曆台灣時區與跨日時間

圖 4:從已建立的 Google 日曆事件開啟詳細資料,可確認跨日的開始、結束時間與 GMT+08:00 台北標準時間。

3. 補上夏令時間邊界測試

台灣目前沒有實施夏令時間,但日期驗證模組仍加入相關防護:遇到時鐘向前調整而不存在的時間,或向後調整而重複出現的時間,都會拒絕輸入。

這部分使用 America/New_York 的時區案例做自動測試;實際 /add 仍固定採用台灣時區。測試也在不同系統時區下執行,確認結果不受電腦設定影響。

Day 22|新增結果、API 錯誤與重複提交

1. 等待 API 結果,再回覆新增成功

Google API 需要一些處理時間,因此指令先使用 deferReply(),完成後再更新回覆。

成功訊息統一顯示行程名稱、日期、開始與結束時間、時區,以及 API 回傳的 htmlLink。回覆設定為僅提交者可見。

只有 API 新增成功,或確實查到已存在的事件,才會顯示對應結果。如果無法確認是否寫入,就明確告知使用者目前無法確認,不會直接回覆「新增成功」。

2. API 失敗與授權失效要有清楚提示

Service 會分類授權、權限、配額、資料與網路等錯誤,再由 Discord 指令顯示容易理解的訊息。Google 原始錯誤物件不會直接貼到 Discord,避免夾帶敏感資訊。

為了測試真實錯誤回覆,我使用獨立的驗證程式,刻意安排兩個情境:

  • 使用不存在的 Calendar ID,確認 Google API 失敗時會回覆「找不到指定的 Google 行事曆」。
  • 使用獨立、故意無效的 Access Token,確認授權失效時會提示重新授權。

這兩種情況都有實際呼叫 Google API,沒有刪除或撤銷原本的 token.json 授權。網路異常、權限不足、配額限制及伺服器錯誤等其他分支,則由模擬 API 自動測試驗證。

Day 22:Google API 失敗及授權失效回覆

圖 5:刻意觸發 Google API 與授權錯誤後,Discord 顯示對應提示,沒有洩漏 Token。

3. 重複提交不應多建一筆

如果使用者按太快,或遇到網路延遲後重試,單純再次呼叫 events.insert() 可能會產生重複行程。

這次使用伺服器、使用者、標題、開始與結束時間等資料產生固定事件 ID。同一使用者在同一伺服器提交完全相同的內容,會對應到同一個事件。

處理方式如下:

  1. 先查詢固定事件 ID,若已存在,就回傳既有行程。
  2. 同時提交的相同請求共用正在進行的操作。
  3. 若新增遇到衝突、逾時或伺服器錯誤,再用事件 ID 查詢確認。
  4. 仍無法確認時,提示使用者稍後以相同內容重試。

我在重啟 Bot 後,再送出相同的跨日行程。Discord 顯示「此行程已存在,未重複新增」,Google API 也確認只有一筆。

Day 22:重複提交回傳既有行程

圖 6:再次提交相同行程時,Bot 回傳既有事件及連結,沒有新增第二筆。

這個規則以「相同內容」為準;修改標題或時間會視為另一筆行程。它也不會自動辨識舊版程式或手動建立的相同行程。

另外,Google 日曆可能將跨日事件分別顯示在兩天,標示「第 1 天/第 2 天,共 2 天」。那是同一筆事件跨兩天顯示,不代表新增了兩筆。

4. 失敗後,Bot 仍要能繼續使用

錯誤回覆正常還不夠,我也確認失敗後的下一次操作能成功。

在完成故障測試後,再新增 Day22_Recovery_AfterFailure,Discord 回覆成功,Google 日曆也確實出現該事件。

Day 22:失敗後仍可新增行程

圖 7:失敗測試後仍能新增行程,並取得新的事件連結。

接著再次觸發錯誤並執行 /ping,Bot 仍正常回覆 Pong!。

Day 22:失敗後 ping 仍正常

圖 8:API 與授權錯誤沒有讓 Bot 結束,後續 /ping 仍可使用。

這三天讓我更清楚:串接 API 不只是把資料送出去,還要先確認輸入合理、正確處理時區,並在成功或失敗時給使用者明確的結果。


*提醒邦友,使用第三方服務/API 時,請務必評估資安風險與隱私保護
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言